using System.Collections;
using System.Collections.Generic;
using NodeCanvas.Framework;
using UnityEngine;
using Yoozoo.Gameplay.Liberty.AI;
using Yoozoo.Gameplay.Liberty.AI.Node;

namespace com.yoozoo.gta
{
    public class NpcSearchForTarget : NavMeshMovement
    {
        public enum PatrolType
        { 
            Sequence,   // Traverse in order of the list.
            Random,     // Traverse in a random order.
            Directional // Traverse to the next waypoint most in front of the current Transform.
        }
        public float StartGiveUpDistance = 5;
        public float RandomWaypointProbability = 0.1f;
        public BBParameter<int> WaypointId;
        public AnimationCurve GiveUpCurve= new AnimationCurve(new Keyframe[2] { new Keyframe(0, 0), new Keyframe(1, 1) });
        public bool IsRandom;
        private int m_WaypointIndex = -1;
        private Vector3 m_EndPosition;
        private PathData _pathData;
        private List<PathPointData> m_Waypoints;
        public PatrolType m_PatrolType;
        protected override void PreOnExecute()
        {
            if (_entity.BirthData.isPatrol)
            {
                speed.value = _entity.BirthData.PatrolSpeed;
            }

            base.PreOnExecute();
        }

        protected override void OnExecute()
        {
            base.OnExecute();
            m_EndPosition = - _transform.position + Vector3.one;
            int wayId = WaypointId.value;
            if (_entity.BirthData.isPatrol)
            {
                wayId = _entity.BirthData.PartrolWayId;
            }
            
            _pathData = LibertyAIUtils.TrafficManager.PathManager.GetPath(wayId);
            m_Waypoints = _pathData.pathPoints;
            m_WaypointIndex = -1;
            if (m_Waypoints.Count > 0) {
                // Do not get a new waypoint position if the task is starting again immediately after it was stopped.
                if (m_WaypointIndex == -1 || (m_EndPosition - _transform.position).sqrMagnitude > 0.5f) {
                    if (m_PatrolType == PatrolType.Random) {
                        m_WaypointIndex = Random.Range(0, m_Waypoints.Count);
                    } else { // Sequence or Directional. Move towards the closest waypoint.
                        var distance = Mathf.Infinity;
                        float localDistance;
                        for (int i = 0; i < m_Waypoints.Count; ++i) {
                            if ((localDistance = Vector3.Magnitude(_transform.position - m_Waypoints[i].position)) < distance) {
                                distance = localDistance;
                                m_WaypointIndex = i;
                            }
                        }
                    }
                }
                SetDestination(m_Waypoints[m_WaypointIndex].position);
            }
        }

        protected override void OnUpdate()
        {
            // Stay idle if the agent does not have any waypoints.
            if (m_Waypoints.Count == 0) {
                EndAction(false);
            }

            // Move to the next waypoint if either of the following occurs:
            // - The agent arrives at the destination.
            // - The agent gives up and moves onto the next waypoint. The closer the agent gets to the destination the higher the chances are they will give up. This prevents the agent
            // from always moving into a room when there is a higher likelyhood that nobody is there the closer they get to the middle of the room.
            if (HasArrived()) {
                SetDestination(NextWaypoint());
            } else {
                var distance = (_transform.position - m_Waypoints[m_WaypointIndex].position).magnitude;
                if ((distance < StartGiveUpDistance && Random.value < GiveUpCurve.Evaluate(1 - (distance / StartGiveUpDistance)))) {
                    SetDestination(NextWaypoint());
                }
            }
        }

      
        private Vector3 NextWaypoint()
        {
            if (m_PatrolType == PatrolType.Random) {
                m_WaypointIndex = Random.Range(0, m_Waypoints.Count);
            } else if (m_PatrolType == PatrolType.Sequence) {
                m_WaypointIndex = (m_WaypointIndex + 1) % m_Waypoints.Count;
            } else {
                // Pick a waypoint that is most facing the same direction that the character is already facing.
                var bestIndex = m_WaypointIndex;
                var bestDotProduct = float.NegativeInfinity;
                float dotProduct;
                for (int i = 0; i < m_Waypoints.Count; ++i) {
                    var direction = (m_Waypoints[i].position - _transform.position);
                    direction.y = 0;
                    
                    // The higher the dot product the more in front of the current Transform the waypoint is.
                    if ((dotProduct = Vector3.Dot(_transform.forward, direction.normalized)) > bestDotProduct) {
                        bestIndex = i;
                        bestDotProduct = dotProduct;
                    }

                    // Break out of the loop if the waypoint is randonmly chosen. This will give the AI some variety.
                    if (Random.value < RandomWaypointProbability) {
                        bestIndex = i;
                        break;
                    }
                }
                m_WaypointIndex = bestIndex;
            }
            return m_Waypoints[m_WaypointIndex].position;
        }

        protected override void OnStop(bool interrupted)
        {
            base.OnStop(interrupted);
            m_EndPosition = _transform.position;
        }
    }
}
